package cn.haohaowo.noteCount;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;

/**
 * 
 * @author luwancai
 *
 */
@Mojo(name="notecount")
public class NoteCountMojo extends AbstractMojo {
	
	private static final String[] INCLUDES_DEFAULT = {"java", "xml", "properties"};
	
	
	@Parameter(readonly=true, required=true) 
	private File basedir;
	
	@Parameter(readonly=true, required=true)
	private File sourceDirectory;
	
	@Parameter(readonly=true, required=true)
	private File testSourceDirectory;
	
	@Parameter(readonly=true, required=false)
	private File resources;
	
	@Parameter(readonly=true, required=false)
	private File testResources;
	
	@Parameter
	private String[] includes;
	
	

	@Override
	public void execute() throws MojoExecutionException, MojoFailureException {
		if(null == includes || 0 == includes.length) {
			includes = INCLUDES_DEFAULT;
		}
		
		try {
			countDir(sourceDirectory);
			countDir(testSourceDirectory);
			countDir(resources);
			countDir(testResources);
		} catch(IOException e) {
			
			throw new MojoExecutionException("Unable to count notes of code.", e); 
		}
	}
	
	private void countDir(File dir) throws IOException {
		try {
			if(!dir.exists()) {
				return ;
			}

			List<File> collected = new ArrayList<File>();
			collectFile(collected, dir);
			
			Integer fileLines = 0;
			Integer noteLines = 0;
			Integer lines = 0;
			Map<String, Integer> countMap = new HashMap<String, Integer>();
			BigDecimal rate = new BigDecimal("0.00");
			BigDecimal totalRate = new BigDecimal("0.00");
			List<BigDecimal> rateList = new ArrayList<BigDecimal>();
			BigDecimal svgRate = new BigDecimal("0.00");
			BigDecimal variRate = new BigDecimal("0.00");
			for(File sourceFile: collected) {
				countMap = countLine(sourceFile);
				fileLines = countMap.get("fileLines");
				noteLines = countMap.get("noteLines");
				lines += fileLines;
//				getLog().info("fileLines ===>>> " + fileLines);
//				getLog().info("noteLines ===>>> " + noteLines);
				if(0 != fileLines) {
					rate = new BigDecimal(noteLines).divide(new BigDecimal(fileLines), 4, BigDecimal.ROUND_HALF_DOWN);
					rateList.add(rate);
					totalRate = totalRate.add(rate);
				}
				
				getLog().info(sourceFile.getAbsolutePath() + ":" + fileLines + " lines of code in this files! The note rate is " + rate);
			}
			
			svgRate = totalRate.divide(new BigDecimal(rateList.size()), 4, BigDecimal.ROUND_HALF_DOWN);
			
			for(BigDecimal rateTemp : rateList) {
				variRate = variRate.add(rateTemp.subtract(svgRate).multiply(rateTemp.subtract(svgRate)));
			}
			
			Double standVariRate = Math.sqrt(variRate.divide(new BigDecimal(rateList.size()), 4, BigDecimal.ROUND_HALF_DOWN).doubleValue());
			
			String path = dir.getAbsolutePath().substring(basedir.getAbsolutePath().length());
			getLog().info(path + ": " + lines + " lines of code in " + collected.size() + " files!");
			getLog().info(path + ": svgRate is " + svgRate + " and standVariRate is " + new BigDecimal(standVariRate).setScale(4, BigDecimal.ROUND_HALF_DOWN) + " !");
		} catch(IOException e) {
			e.printStackTrace();
		}
	}

	private Map<String, Integer> countLine(File file) throws IOException {
		Integer fileLines = 0;
		Integer noteLines = 0;
		Boolean isStart = false;
		Integer noteStartLineNum = 0;
		Integer noteEndLineNum = 0;
		String readLineStr = null;
		Map<String, Integer> countMap = new HashMap<String, Integer>();
		BufferedReader reader = null;
		
		try {
			reader = new BufferedReader(new FileReader(file));
			while(reader.ready()) {
				readLineStr = reader.readLine().trim();
				fileLines++;
				
				if(!readLineStr.isEmpty()) {
					if(file.getName().endsWith(".java")) {
						if(readLineStr.matches("^(^\\\")*(////).*$")) {
							noteLines++;
							continue;
						}
						
						if(readLineStr.matches("^(^\\\")*(///*).*$") && !isStart) {
							isStart = true;
							noteStartLineNum = fileLines;
						}
						
						if(readLineStr.matches("^(^\\\")*(/*//).*$") && isStart) {
							isStart = false;
							noteEndLineNum = fileLines - noteStartLineNum;
							if(0 == noteEndLineNum) {
								noteLines += 1;
							} else if(0 < noteEndLineNum) {
								noteLines += noteEndLineNum;
							}
						}
						
//						getLog().info("file name ===>>> " + file.getName() + " str ===>>> " + readLineStr);
					}
					
					if(file.getName().endsWith(".xml")) {
						if(readLineStr.startsWith("<!--") && !isStart) {
							isStart = true;
							noteStartLineNum = fileLines;
						}
						
						if(readLineStr.endsWith("-->") && isStart) {
							isStart = false;
							noteEndLineNum = fileLines - noteStartLineNum;
							if(0 == noteEndLineNum) {
								noteLines += 1;
							} else if(0 < noteEndLineNum) {
								noteLines += noteEndLineNum;
							}
						}
						
//						getLog().info("file name ===>>> " + file.getName() + " str ===>>> " + readLineStr);
					}
					
					if(file.getName().endsWith(".properties")) {
						if(readLineStr.matches("^.*(\\#).*$")) {
							noteLines++;
							continue;
						}
					}
				}
			}
		} catch(IOException e) {
			e.printStackTrace();
		} finally{
			reader.close();
		}
		countMap.put("noteLines", noteLines);
		countMap.put("fileLines", fileLines);
		
		return countMap;
	}

	private void collectFile(List<File> collected, File file) {
		if(file.isFile()) {
			for(String include : includes) {
				if(file.getName().endsWith("." + include)) {
					collected.add(file);
					break;
				}
			}
		} else {
			for(File sub : file.listFiles()) {
				collectFile(collected, sub);
			}
		}
	}
	
}
